﻿using Microsoft.IdentityModel.JsonWebTokens;
using Microsoft.IdentityModel.Tokens;
using System.IdentityModel.Tokens.Jwt;
using System.Security.Claims;
using System.Text;
using System.Text.Json;
using JwtRegisteredClaimNames = Microsoft.IdentityModel.JsonWebTokens.JwtRegisteredClaimNames;

namespace CTP.CRM.Core.Helper
{
    /// <summary>
    /// JWT 加解密
    /// </summary>
    public class JwtService
    {
        private static string GenerateEncryptedToken(SigningCredentials signingCredentials, IDictionary<string, object> payload)
        {
            var expires = CTPABPCore.Configuration["JWTSettings:ExpiredTime"];
            var issuer = CTPABPCore.Configuration["JWTSettings:ValidIssuer"];
            var audience = CTPABPCore.Configuration["JWTSettings:ValidAudience"];

            var datetimeOffset = DateTimeOffset.UtcNow;

            if (!payload.ContainsKey(JwtRegisteredClaimNames.Iat))
                payload.Add(JwtRegisteredClaimNames.Iat, datetimeOffset.ToUnixTimeSeconds());

            if (!payload.ContainsKey(JwtRegisteredClaimNames.Nbf))
                payload.Add(JwtRegisteredClaimNames.Nbf, datetimeOffset.ToUnixTimeSeconds());

            if (!payload.ContainsKey(JwtRegisteredClaimNames.Exp))
            {
                var minute = string.IsNullOrWhiteSpace(expires) ? 20 : Convert.ToDouble(expires);
                payload.Add(JwtRegisteredClaimNames.Exp, DateTimeOffset.UtcNow.AddMinutes(minute).ToUnixTimeSeconds());
            }

            if (!payload.ContainsKey(JwtRegisteredClaimNames.Iss))
                payload.Add(JwtRegisteredClaimNames.Iss, issuer);

            if (!payload.ContainsKey(JwtRegisteredClaimNames.Aud))
                payload.Add(JwtRegisteredClaimNames.Aud, audience);

            var jwtToken = new JsonWebTokenHandler().CreateToken(JsonSerializer.Serialize(payload), signingCredentials);

            return jwtToken;
        }

        private static string GenerateEncryptedToken(SigningCredentials signingCredentials, IEnumerable<Claim> claims)
        {
            var expires = CTPABPCore.Configuration["JWTSettings:ExpiredTime"];
            var token = new JwtSecurityToken(
               claims: claims,
               expires: DateTime.UtcNow.AddMinutes(Convert.ToInt32(expires)),
               signingCredentials: signingCredentials);
            var tokenHandler = new JwtSecurityTokenHandler();
            return tokenHandler.WriteToken(token);
        }

        private static SigningCredentials GetSigningCredentials()
        {
            byte[] secret = Encoding.UTF8.GetBytes(CTPABPCore.Configuration["JWTSettings:IssuerSigningKey"]);
            return new SigningCredentials(new SymmetricSecurityKey(secret), SecurityAlgorithms.HmacSha256);
        }

        /// <summary>
        /// 生成 Token
        /// </summary>
        /// <param name="payload">Claims</param>
        /// <param name="expiredTime">过期时间（分钟）</param>
        /// <returns></returns>
        public static string GetToken(IDictionary<string, object> payload, long? expiredTime = null)
        {
            payload = CombinePayload(payload, expiredTime);
            var jwtToken = GenerateEncryptedToken(GetSigningCredentials(), payload);
            return jwtToken;
        }

        public static string GetToken(IEnumerable<Claim> claims, long? expiredTime = null)
        {
            var jwtToken = GenerateEncryptedToken(GetSigningCredentials(), claims);
            return jwtToken;
        }

        /// <summary>
        /// 解密Token
        /// </summary>
        /// <param name="input"></param>
        /// <returns></returns>
        public static Dictionary<string, object> Decode(string input)
        {
            if (input.Contains("Bearer"))
                input = input.ReplaceFirst("Bearer", "").TrimStart();

            var secretKey = Encoding.UTF8.GetBytes(CTPABPCore.Configuration["JWTSettings:IssuerSigningKey"]);
            Dictionary<string, object> list = new Dictionary<string, object>();

            //解密方法
            JsonWebTokenHandler tokenHandler = new JsonWebTokenHandler();
            TokenValidationParameters varParam = new TokenValidationParameters();
            var securityKey = new SymmetricSecurityKey(secretKey);
            varParam.IssuerSigningKey = securityKey;
            varParam.ValidateIssuer = false;
            varParam.ValidateAudience = false;
            varParam.ClockSkew = TimeSpan.Zero;
            var claimsPrincipal = tokenHandler.ValidateToken(input, varParam);
            //输出结果
            foreach (var item in claimsPrincipal.Claims)
            {
                list.Add(item.Key, item.Value);
            }
            return list;
        }

        /// <summary>
        /// 组合 Claims 负荷
        /// </summary>
        /// <param name="payload">Claims</param>
        /// <param name="expiredTime">过期时间，单位：分钟</param>
        /// <returns></returns>
        private static IDictionary<string, object> CombinePayload(IDictionary<string, object> payload, long? expiredTime = null)
        {
            return payload;
        }
    }
}
